const express = require('express');
const cookieParser = require('cookie-parser');
const session = require('express-session');
const app = express();
const fs = require('fs');
const path = require('path');

const https = require('https');

const port = 443;

// 设置当前目录为项目根目录, 这样做是为了后面可以使用相对路径读取到私钥和证书
const dir = path.join(__dirname);
process.chdir(dir);
console.log(`当前工作目录为${dir}`);

// 使用cookie-parser中间件, 要不然无法获取cookie
app.use(cookieParser());

// 设置静态资源目录
app.use(express.static('static'));

app.use(session({
    secret: '7b09c869cf04011d6af56827bf1d916890f30caf52ff631113bfeb8c5d129b459ec258e1a6642d640e7725d0282305c8fc941bdca15d2e719f6bdc938de232e3', // 此秘钥用于对 session 数据进行签名, 用户防止客户端修改sessionId，冒充其他用户
    resave: false, // 是否在每次请求时都重新保存会话数据到会话存储中，即便会话数据在请求期间并没有发生改变。
    // 设置成false表示只有当会话数据在请求期间发生了实际变化时，才会将更新后的会话数据保存到存储中。这样可以减少不必要的存储操作，降低服务器的负载，提高性能。
    saveUninitialized: true, // 是否保存未初始化的 session，未初始化的会话指的是新创建但还没有被修改过的会话。
    // 设置成false表示只有当会话数据被实际修改后，才会将其保存到存储中。这样可以避免存储大量未使用的会话数据，节省存储空间。
    // 如果secure设置为false的同时，sameSite为none, 浏览器不会保存cookie，每次都会生成一个新的sessionId
    // 所以为了演示csrf攻击，我们必须要将secure设置为true，将sameSite设置为none，也就是说我们必须要使用https协议才能完成csrf攻击的演示
    cookie: {
        secure: true, // 是否只在 HTTPS 协议下传输 cookie
        sameSite: 'strict' // 该属性用于防止 CSRF 攻击，如果不设置，高版本的浏览器默认设置为lax
    } // 设置 cookie 的属性
}));

// 读取证书和私钥文件
const options = {
    key: fs.readFileSync('../cert/private-key.pem'),
    cert: fs.readFileSync('../cert/certificate.pem')
};

// 登录接口
app.get('/login', (req, res) => {
    let sessionId = req.sessionID;
    console.log(`sessionId为${sessionId}`);
    // 模拟登录操作
    req.session.username = "张三-用户";
    res.send(`Logged in successfully, sessionId为${sessionId}`);
});

// 敏感操作接口（模拟转账）
app.get('/transfer', (req, res) => {
    console.log(`请求来源referer：${req.headers.referer}`);
    const sessionId = req.sessionID;
    // 检查用户是否已登录
    if (!req.session.username) {
        console.log(`本次请求的sessionId为${sessionId},用户未登录`);
        return res.status(401).send('用户未登录');
    }
    console.log(`本次请求的sessionId为${sessionId},用户${req.session.username}的钱正在转到别人的账户`);
    // 模拟转账操作
    console.log('开始转账...');
    res.send('转账成功');
});

// 创建 HTTPS 服务器
const server = https.createServer(options, app);

// 监听端口
server.listen(port, () => {
    console.log(`银行的服务器正在运行在 https://localhost:${port}`);
});